﻿/*! @file */
//2007.09.30 kobake CDocLineMgr から分離
/*
	Copyright (C) 2008, kobake
	Copyright (C) 2018-2022, Sakura Editor Organization

	SPDX-License-Identifier: Zlib
*/
#ifndef SAKURA_CWORDPARSE_C025B28F_9FBB_464A_9831_5E9DDAAEAA35_H_
#define SAKURA_CWORDPARSE_C025B28F_9FBB_464A_9831_5E9DDAAEAA35_H_
#pragma once

#include "basis/SakuraBasis.h"
class CNativeW;

//! 文字種類識別子
enum ECharKind{
	CK_NULL,			//!< NULL
	CK_TAB,				//!< タブ 0x9<=c<=0x9
	CK_CR,				//!< CR = 0x0d 
	CK_LF,				//!< LF = 0x0a 
	CK_CTRL,			//!< 上記以外の c<0x20

	CK_SPACE,			//!< 半角のスペース 0x20<=c<=0x20
	CK_CSYM,			//!< 識別子に使用可能な文字 (英数字、アンダースコア)
	CK_KATA,			//!< 半角のカタカナ 0xA1<=c<=0xFD
	CK_LATIN,			//!< ラテン１補助、ラテン拡張のうちアルファベット風のもの 0x00C0<=c<0x0180
	CK_UDEF,			//!< ユーザー定義キーワード文字（#$@\）
	CK_ETC,				//!< 半角のその他

	CK_ZEN_SPACE,		//!< 全角スペース
	CK_ZEN_NOBASU,		//!< 伸ばす記号 0x815B<=c<=0x815B 'ー'
	CK_ZEN_DAKU,		//!< 全角濁点 0x309B<=c<=0x309C 「゛゜」
	CK_ZEN_CSYM,		//!< 全角版、識別子に使用可能な文字 (英数字、アンダースコア)

	CK_ZEN_KIGO,		//!< 全角の記号
	CK_HIRA,			//!< ひらがな
	CK_ZEN_KATA,		//!< 全角カタカナ
	CK_GREEK,			//!< ギリシャ文字
	CK_ZEN_ROS,			//!< ロシア文字:
	CK_ZEN_SKIGO,		//!< 全角の特殊記号
	CK_ZEN_ETC,			//!< 全角のその他（漢字など）
};

class CWordParse{
public:
	//2001.06.23 N.Nakatani
	//2007.09.30 kobake     CDocLineMgrから移動
	/*!
		@brief 現在位置の単語の範囲を調べる staticメンバ
		@author N.Nakatani
		@retval true	成功 現在位置のデータは「単語」と認識する。
		@retval false	失敗 現在位置のデータは「単語」とは言いきれない気がする。
	*/
	static bool WhereCurrentWord_2(
		const wchar_t*	pLine,			//[in]  調べるメモリ全体の先頭アドレス
		CLogicInt		nLineLen,		//[in]  調べるメモリ全体の有効長
		CLogicInt		nIdx,			//[in]  調査開始地点:pLineからの相対的な位置
		bool			bEnableExtEol,	//[in]  Unicode改行文字を改行とみなすかどうか
		CLogicInt*		pnIdxFrom,		//[out] 単語が見つかった場合は、単語の先頭インデックスを返す。
		CLogicInt*		pnIdxTo,		//[out] 単語が見つかった場合は、単語の終端の次のバイトの先頭インデックスを返す。
		CNativeW*		pcmcmWord,		//[out] 単語が見つかった場合は、現在単語を切り出して指定されたCMemoryオブジェクトに格納する。情報が不要な場合はNULLを指定する。
		CNativeW*		pcmcmWordLeft	//[out] 単語が見つかった場合は、現在単語の左に位置する単語を切り出して指定されたCMemoryオブジェクトに格納する。情報が不要な場合はNULLを指定する。
	);

	//! 現在位置の文字の種類を調べる
	static ECharKind WhatKindOfChar(
		const wchar_t*	pData,
		int				pDataLen,
		int				nIdx
	);

	//! 二つの文字を結合したものの種類を調べる
	static ECharKind WhatKindOfTwoChars(
		ECharKind		kindPre,
		ECharKind		kindCur
	);

	//! 二つの文字を結合したものの種類を調べる for 強調キーワード
	static ECharKind WhatKindOfTwoChars4KW(
		ECharKind		kindPre,
		ECharKind		kindCur
	);

	//	pLine（長さ：nLineLen）の文字列から次の単語を探す。探し始める位置はnIdxで指定。
	static bool SearchNextWordPosition(
		const wchar_t*	pLine,
		CLogicInt		nLineLen,
		CLogicInt		nIdx,		//	桁数
		CLogicInt*		pnColumnNew,	//	見つかった位置
		BOOL			bStopsBothEnds	//	単語の両端で止まる
	);

	//	pLine（長さ：nLineLen）の文字列から次の単語を探す。探し始める位置はnIdxで指定。 for 強調キーワード
	static bool SearchNextWordPosition4KW(
		const wchar_t*	pLine,
		CLogicInt		nLineLen,
		CLogicInt		nIdx,		//	桁数
		CLogicInt*		pnColumnNew,	//	見つかった位置
		BOOL			bStopsBothEnds	//	単語の両端で止まる
	);

	static bool SearchPrevWordPosition(const wchar_t* pLine,
		CLogicInt nLineLen, CLogicInt nIdx, CLogicInt* pnColumnNew, BOOL bStopsBothEnds);

	template< class CHAR_TYPE >
	static int GetWord( const CHAR_TYPE* pS, const int nLen, const CHAR_TYPE *pszSplitCharList,
		CHAR_TYPE **ppWordStart, int *pnWordLen );

protected:

	static bool _match_charlist( const ACHAR c, const ACHAR *pszList );
	static bool _match_charlist( const WCHAR c, const WCHAR *pszList );
};

/** 指定アドレスが URL の先頭ならば TRUE とその長さを返す。
    @param[in]  pszLine    文字列バッファの先頭アドレス
    @param[in]  offset     URL 判定開始文字を示す、pszLine からの相対位置。
    @param[in]  nLineLen   URL 判定最終文字の次を示す、pszLine からの相対位置。
    @param[out] pnMatchLen URL の長さを受け取る変数のアドレス。NULL可。長さとは pszLine + offset からの距離。

    境界判定はメールアドレスの先頭でのみ行われ、URL の先頭ではこれまで通り行われません。
*/
BOOL IsURL( const wchar_t* pszLine, int offset, int nLineLen, int* pnMatchLen);

/** @deprecated 互換性のために残されています。offset 引数が追加されたものを使用してください。
*/
inline
BOOL IsURL( const wchar_t* pszLine, int nLineLen, int* pnMatchLen)
{
	return IsURL(pszLine, 0, nLineLen, pnMatchLen);
}

/** 指定アドレスがメールアドレスの先頭ならば TRUE とその長さを返す。
    @param[in]  pszBuf          文字列バッファの先頭アドレス
    @param[in]  offset          メールアドレス判定開始文字を示す、pszBuf からの相対位置。
    @param[in]  nBufLen         メールアドレス判定最終文字の次を示す、pszBuf からの相対位置。
    @param[out] pnAddressLength メールアドレスの長さを受け取る変数のアドレス。NULL可。長さとは pszBuf + offset からの距離。

    正の offset が与えられた場合は、その場合に限り、判定開始位置直前の文字との間で境界判定を行います。
    途中から切り出したメールアドレスの一部をメールアドレスであると誤って判定しないために
    pszBuf を固定し offset を０以上の範囲で変化させるのが望ましい使用方法です。
*/
BOOL IsMailAddress( const wchar_t* pszBuf, int offset, int nBufLen, int* pnAddressLength);

/** @deprecated 互換性のために残されています。offset 引数が追加されたものを使用してください。
*/
inline
BOOL IsMailAddress( const wchar_t* pszBuf, int nBufLen, int* pnAddressLength)
{
	return IsMailAddress(pszBuf, 0, nBufLen, pnAddressLength);
}

// ACHAR 版
inline bool CWordParse::_match_charlist( const ACHAR c, const ACHAR *pszList )
{
	for( int i = 0; pszList[i] != '\0'; i++ ){
		if( pszList[i] == c ){ return true; }
	}
	return false;
}
// WCHAR 版
inline bool CWordParse::_match_charlist( const WCHAR c, const WCHAR *pszList )
{
	for( int i = 0; pszList[i] != L'\0'; i++ ){
		if( pszList[i] == c ){ return true; }
	}
	return false;
}

/*!
	@param [in] pS					文字列バッファ
	@param [in] nLen				文字列バッファの長さ
	@param [in] pszSplitCharList	区切り文字たち
	@param [out] ppWordStart		単語の開始位置
	@param [out] pnWordLen			単語の長さ

	@return 読んだデータの長さ。
*/
template< class CHAR_TYPE >
int CWordParse::GetWord( const CHAR_TYPE *pS, const int nLen, const CHAR_TYPE *pszSplitCharList,
	CHAR_TYPE **ppWordStart, int *pnWordLen )
{
	const CHAR_TYPE *pr = pS;
	CHAR_TYPE *pwordstart;
	int nwordlen;

	if( nLen < 1 ){
		pwordstart = const_cast<CHAR_TYPE *>(pS);
		nwordlen = 0;
		goto end_func;
	}

	// 区切り文字をスキップ
	for( ; pr < pS + nLen; pr++ ){
		// 区切り文字でない文字の間ループ
		if( !_match_charlist(*pr, pszSplitCharList) ){
			break;
		}
	}
	pwordstart = const_cast<CHAR_TYPE*>(pr);   // 単語の先頭位置を記録

	// 単語をスキップ
	for( ; pr < pS + nLen; pr++ ){
		// 区切り文字がくるまでループ
		if( _match_charlist(*pr, pszSplitCharList) ){
			break;
		}
	}
	nwordlen = pr - pwordstart;  // 単語の長さを記録

end_func:
	if( ppWordStart ){
		*ppWordStart = pwordstart;
	}
	if( pnWordLen ){
		*pnWordLen = nwordlen;
	}
	return pr - pS;
}
#endif /* SAKURA_CWORDPARSE_C025B28F_9FBB_464A_9831_5E9DDAAEAA35_H_ */
